/** processor.h
  * tomasulo pipelined processor
  * Author: Long Tran
  * Email:  ltran3@gatech.edu
  * Date created: Mar 19 2011
  */

#ifndef PROCESSOR_H
#define PROCESSOR_H

#include <iostream>
#include <vector>
#include <queue>

typedef unsigned int uint;

class Instruction
{
  int tag_;                      // id (and dest tag)
  uint addr_;                    // address
  int type_;                     // function unit type
  int dest_, src1_, src2_;       // dest, srs1, src2 registers
  int src1Tag_, src2Tag_;        // src1, src2 tag
  bool src1Ready_, src2Ready_;   // ready bits

  friend class RegisterFile;
  friend class Processor;
  friend class SchedQ;
public:
  // timing information
  int fetchTime_;
  int dispatchTime_;
  int scheduleTime_;
  int executeTime_;
  int updateTime_;

public:
  Instruction()
    : tag_(-1), dest_(-1), src1_(-1), src2_(-1), src1Tag_(-1), src2Tag_(-1), src1Ready_(false), src2Ready_(false)
  { fetchTime_ = dispatchTime_ = scheduleTime_ = executeTime_ = updateTime_ = -1; }
  Instruction(int tag)
    : tag_(tag), dest_(-1), src1_(-1), src2_(-1), src1Tag_(-1), src2Tag_(-1), src1Ready_(false), src2Ready_(false)
  { fetchTime_ = dispatchTime_ = scheduleTime_ = executeTime_ = updateTime_ = -1; }

  /** read instruction from stream in format: addr type dest src1 src2 */
  void read(std::istream& input);

  /** getters */
  int tag() const { return tag_; }
  uint addr() const { return addr_; }
  int type() const { return type_; }
  int dest() const { return dest_; }
  int src1() const { return src1_; }
  int src2() const { return src2_; }
};

class Program : public std::vector<Instruction>
{
public:
  /** get the next instruction of the program from input stream, return instruction id */
  int getNext(std::istream& input) {
    push_back(Instruction());
    back().read(input);
    if (back().type() == -1) {
      this->pop_back();
      return -1;
    }
    else
      return size()-1;
  }
  /** print all logs */
  void output();
};

/** Dispatch queue */
class DispatchQ : public std::vector<int>
{
protected:
  Program* program_;
  int size_;
  std::vector<bool> empty_;
  std::queue<int> emptySlot_;
  int verbose_;
public:
  DispatchQ(Program& program, int size, int verbose = 0)
    : std::vector<int>(size, -1), program_(&program),
      size_(size), empty_(size, true), verbose_(verbose)
  {}
  bool isEmpty(int i) { return empty_[i]; }

  /** Fetch up to N instructions from input stream */
  bool fetch(std::istream& input, int N);

  /** access i-th instruction */
  Instruction& instRef(int i) { return program_->at(this->at(i)); }
  const Instruction& inst(int i) const { return program_->at(this->at(i)); }

  /** clear a slot in the queue */
  void clearSlot(int i) { this->at(i) = -1; empty_.at(i) = true; }
  void markEmptySlots() {
    emptySlot_ = std::queue<int>();
    for (uint i = 0; i < size(); i++) if (isEmpty(i)) emptySlot_.push(i);
  }

  /** sort queue by instructions' tags */
//  void sort();

  /** dumping queue's entry information */
  void dump();
};

/** Processor's registers */
class Register
{
  int id_;            // Register number
  bool ready_;        // ready bit
  int tag_;           // current tag
public:
  Register(int id = 0)
  : id_(id), ready_(true), tag_(-1) {}

  /** Assign new tag to a register */
  void newTag(int tag) { ready_ = false; tag_ = tag; }

  /** When a register is ready in result bus */
  void ready(int tag) { if (tag_ == tag) ready_ = true; }

  /** getters */
  bool isReady() const { return ready_; }
  int tag() const { return tag_; }

  /** This should be the same as array index of register file */
  int regNo() const { return id_; }
};

class RegisterFile : public std::vector<Register>
{
  int verbose_;
public:
  RegisterFile(int verbose = 0) : verbose_(verbose) {}
  /** Populate tagging information from register file to scheduling queue entry
   *  and assign new tag to the destination register
   */
  void processTag(Instruction& inst);
};

class SchedQ : public DispatchQ
{
  RegisterFile* regFile_;        // needed to access register file
  int type_;                     // function unit type
public:
  SchedQ(Program& program, RegisterFile& regFile, int type, int size, int verbose = 0)
    : DispatchQ(program, size, verbose), regFile_(&regFile), type_(type)
  {}

  /** dispatch i-th instruction of the program to scheduling queue */
  bool dispatch(int i);

  /** update a reservation station (queue entry) once a tagged register is ready */
  void update(int rs, int tag);

  /** dumping entries' information */
  void dump();
};

/** The function unit - FUs */
class FunctionUnit : public std::queue<int>
{
  Program* program_;          // needed to access instruction
  int type_;                  // function unit type
  int cycle_;                 // number of cycles needed
  int verbose_;
public:
  FunctionUnit(Program& program, int type, int cycle, int verbose = 0)
    : program_(&program), type_(type), cycle_(cycle), verbose_(verbose) {}

  /** getters */
  int type() const { return type_; }
  int cycle() const { return cycle_; }

  /** fire (issue) i-th instruction of the program */
  bool fire(int i);
};

typedef std::vector<FunctionUnit> scoreboard_type;

/** Result bus */
class CDB
{
public:
  int tag_;    // tag
  int reg_;    // register id
  int time_;   // issue time
  CDB(int tag = -1, int reg = -1, int time = -1) : tag_(tag), reg_(reg), time_(time) {}
};

class Processor
{
public:
protected:
  std::istream& input_;            // input stream
  int k0_, k1_, k2_, D_, m_, N_;   // processor's parameters
  RegisterFile regFile_;           // register file
  Program program_;                // instruction list
  DispatchQ dpQ_;                  // dispatch queue
  std::vector<SchedQ> shQ_;        // scheduling queues
  scoreboard_type fu_;             // function units
  std::vector<CDB> cdb_;           // result buses
  bool change_;                    // detect change
  int verbose_;
public:
  Processor(std::istream& input, int k0, int k1, int k2, int D, int m, int N, int verbose = 0);
  int infiniteLoop();              // process all instructions
protected:
  void updateRegs();
  void fire();
  void dispatch();                 // reserved slots
  void execute();                  // execute function units
  void updateSchedQ();
  void stateUpdate();
  void clearOldCDB();
  void dump();
};

#endif // PROCESSOR_H
